package com.duojuhe.aspect;


import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.duojuhe.common.annotation.RepeatSubmit;
import com.duojuhe.common.constant.SingleStringConstant;
import com.duojuhe.common.constant.SystemConstants;
import com.duojuhe.common.utils.jsonutils.JsonUtils;
import com.duojuhe.redis.redisson.RedisSonLockUtils;
import com.duojuhe.common.exception.base.DuoJuHeException;
import com.duojuhe.common.result.ErrorCodes;
import com.duojuhe.common.utils.encryption.md5.MD5Util;
import com.duojuhe.common.utils.token.TokenUtils;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Objects;
/**
 * 重复提交处理
 *
 * @author echo
 */
@Order(1)
@Slf4j
@Aspect
@Component
public class RepeatSubmitControllerAspect {
    private static final String lockDefaultKey = "defaultKey";
    /**
     * 定义哪些controller需要处理
     */
    @Pointcut("execution(public * com.duojuhe..*.*(..))")
    public void controllerPoint() {

    }

    /**
     * 在目标方法执行前执行
     *
     * @param joinPoint
     */
    @Before("(controllerPoint() && @annotation(repeatSubmit))")
    public void before(final JoinPoint joinPoint, RepeatSubmit repeatSubmit) throws DuoJuHeException {
        //防止短时间重复请求
        if (repeatSubmit != null && repeatSubmit.submitForm()) {
            //获取重复key
            String lockKey = buildRepeatSubmitKey(joinPoint,repeatSubmit);
            log.info("【重复请求获取key】获取防止短时间重复请求key={}", lockKey);
            if (StringUtils.isBlank(lockKey)) {
                //如果加了防重复注解，但是获取的key确为空，则统一返回重复错误
                throw new DuoJuHeException(ErrorCodes.DUPLICATE_SUBMIT_ERROR);
            }
            //过期时间
            long expire = repeatSubmit.expire();
            //获取锁
            boolean lock = RedisSonLockUtils.tryLock(SystemConstants.REPEAT_SUBMIT_KEY+lockKey, expire);
            if (!lock) {
                throw new DuoJuHeException(ErrorCodes.DUPLICATE_SUBMIT_ERROR);
            }
        }
    }


    /**
     * 清除防重复的标记
     *
     * @param joinPoint
     */
    @After("(controllerPoint() && @annotation(repeatSubmit))")
    public void doAfter(JoinPoint joinPoint, RepeatSubmit repeatSubmit) {
        // 处理完请求，返回内容
        if (repeatSubmit != null && repeatSubmit.submitForm()) {
            //获取释放防止重复key
            String lockKey = buildRepeatSubmitKey(joinPoint,repeatSubmit);
            log.info("【重复请求释放key】释放防止短时间重复请求key={}", lockKey);
            if (StringUtils.isNotBlank(lockKey)){
                RedisSonLockUtils.unlock(SystemConstants.REPEAT_SUBMIT_KEY+lockKey);
            }
        }
    }




    /**
     * 构建防止重复的key
     * @param joinPoint
     * @param repeatSubmit
     * @return
     */
    private String buildRepeatSubmitKey(JoinPoint joinPoint, RepeatSubmit repeatSubmit){
        try{
            //获取请求request
            HttpServletRequest request = ((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest();
            String reqUri = request.getRequestURI();
            Object[] args = joinPoint.getArgs();
            //token值
            String token = TokenUtils.getTokenByRequest(request);
            //获取到的token值
            token = StringUtils.isNotBlank(token)?token:"";
            if (args == null) {
                return lockDefaultKey;
            }
            //返回字符串
            StringBuilder noRepeatSubmitStr = new StringBuilder();
            //默认返回key
            String lockKey = StringUtils.EMPTY;
            if (StringUtils.isBlank(repeatSubmit.repeatSubmitKeyParts())){
                // key = md5(请求参数的json请求体字符串 + requestURI)
                for (Object arg : args) {
                    if (!JsonUtils.isJson(arg.toString())){
                        continue;
                    }
                    noRepeatSubmitStr.append(JSON.toJSONString(arg));
                }
                lockKey = MD5Util.getMD532((noRepeatSubmitStr.append(reqUri).append(token).toString()));
            }else{
                for (Object arg : args) {
                    if (!JsonUtils.isJson(arg.toString())){
                        continue;
                    }
                    JSONObject jsonObject = JSONObject.parseObject(JSON.toJSONString(arg));
                    String[] keyPartArray = repeatSubmit.repeatSubmitKeyParts().split(SingleStringConstant.COMMA);
                    for (String keyPart : keyPartArray) {
                        noRepeatSubmitStr.append(jsonObject.getString(keyPart));
                    }
                }
                lockKey = MD5Util.getMD532(noRepeatSubmitStr.append(reqUri).append(token).toString());
            }
            return lockKey;
        }catch (Exception e){
            return StringUtils.EMPTY;
        }
    }
}
